package dnl.confij.xml;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.Collection;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;

import org.apache.commons.io.IOUtils;

import dnl.confij.BaseConfigurationSource;
import dnl.confij.ConfigException;
import dnl.confij.ConfigParam;
import dnl.confij.ConfigSection;
import dnl.confij.Configuration;
import dnl.confij.ParameterType;

/**
 * A properties based configuration source.
 * 
 * @author <a href="mailto:daniel.or@gmail.com">Daniel Or</a>
 * 
 */
public class XmlConfigurationSource extends BaseConfigurationSource {

	private String charset;

	/**
	 * 
	 * Constructs a new <code>PropertiesConfigurationSource</code> using the
	 * given file.
	 * 
	 * @param propertiesFile
	 */
	public XmlConfigurationSource(File xmlFile) {
		super(xmlFile);
	}

	/**
	 * 
	 * Constructs a new <code>PropertiesConfigurationSource</code> using the
	 * given file and the given charset.
	 * 
	 * @param propertiesFile
	 * @param charset
	 *            a character set that corresponds to the file's encoding.
	 */
	public XmlConfigurationSource(File xmlFile, String charset) {
		super(xmlFile);
		this.charset = charset;
	}

	@Override
	protected void loadConfigurationImpl(Configuration config) throws ConfigException {
		InputStream input = null;
		XMLStreamReader reader = null;
		try {
			input = new FileInputStream(getConfigFile());
			XMLInputFactory factory = XMLInputFactory.newInstance();
			reader = factory.createXMLStreamReader(input, charset);

			ConfigSection section = null;
			String paramName = null;
			ParameterType type = null;
			while (reader.hasNext()) {
				int eventType = reader.getEventType();
				if (eventType == XMLStreamConstants.START_DOCUMENT || eventType == XMLStreamConstants.CHARACTERS) {
					reader.next();
					continue;
				}
				String localName = reader.getLocalName();
				if (eventType == XMLStreamConstants.START_ELEMENT && "section".equals(localName)) {
					String sectionName = reader.getAttributeValue(0);
					section = config.getOrCreateSection(sectionName);
				}

				if (eventType == XMLStreamConstants.START_ELEMENT && "param".equals(localName)) {
					int nAtts = reader.getAttributeCount();
					for (int i = 0; i < nAtts; i++) {
						String attributeName = reader.getAttributeLocalName(i);
						if ("name".equals(attributeName)) {
							paramName = reader.getAttributeValue(i);
						}
						if ("type".equals(attributeName)) {
							type = ParameterType.getParameterType(reader.getAttributeValue(i));
						}
					}
				}
				if (eventType == XMLStreamConstants.START_ELEMENT && "value".equals(localName)) {
					String elementText = reader.getElementText();
					section.setParam(paramName, elementText, type);
				}
				reader.next();
			}
		} catch (IOException e) {
			e.printStackTrace();
		} catch (XMLStreamException e) {
			e.printStackTrace();
		} finally {
			try {
				if (reader != null)
					reader.close();
			} catch (XMLStreamException e) {
				e.printStackTrace();
			}
			IOUtils.closeQuietly(input);
		}

	}

	@Override
	protected void saveConfigurationImpl(Configuration config) throws ConfigException {
		PrintWriter fw = null;
		String indent = "    ";
		try {
			fw = new PrintWriter(getConfigFile());
			fw.println("<configuration>");
			Collection<ConfigSection> sections = config.getSections();
			for (ConfigSection configSection : sections) {
				fw.print(indent);	
				fw.print("<section ");
				fw.print("name=\"");
				fw.print(configSection.getName());
				fw.println("\">");
				
				Collection<ConfigParam> configParameters = configSection.getConfigParameters();
				for (ConfigParam configParam : configParameters) {
					fw.print(indent);	
					fw.print(indent);	
					fw.print("<param name=\"");
					fw.print(configParam.getName());
					if(configParam.getParameterType() != null){
						fw.print("\" type=\"");
						fw.print(configParam.getParameterType().name().toLowerCase());
					}
					fw.println("\">");
					fw.print(indent);	
					fw.print(indent);	
					fw.print(indent);	
					fw.print("<value>");	
					fw.print(configParam.getValue());	
					fw.println("</value>");	
					fw.print(indent);	
					fw.print(indent);
					fw.println("</param>");	
				}
				fw.print(indent);
				fw.println("</section>");
			}
			fw.println("</configuration>");
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} finally {
			IOUtils.closeQuietly(fw);
		}
	}

}
